/*
 * CountingMgr.cpp
 *
 *  Created on: Oct 19, 2011
 *      Author: kimi
 */

#include "CountingMgr.h"

CountingMgr::CountingMgr(string filePath) {

	countingFile = new FLRFile(filePath,4*sizeof(unsigned long));

	tamanioGenericoDeRegistros = 4*1024;

	eleccionesHash = new ExtensibleHash(filePath + "_Elecciones_",tamanioGenericoDeRegistros);
	listasEnElecciones = new ExtensibleHash(filePath + "_ListasElecciones_",tamanioGenericoDeRegistros);

	filtroDistritos = new ExtensibleHash(filePath + "_FiltroDistrito_",tamanioGenericoDeRegistros);
	filtroListas = new ExtensibleHash(filePath + "_FiltroListas_",tamanioGenericoDeRegistros);
	filtroElecciones = new ExtensibleHash(filePath + "_FiltroElecciones_",tamanioGenericoDeRegistros);

	listasConIgualNombre = new ExtensibleHash(filePath + "_SimilarLists_ " , tamanioGenericoDeRegistros);
}

CountingMgr::~CountingMgr() {
	// TODO Auto-generated destructor stub
	delete countingFile;
}

bool CountingMgr::addListaEnEleccion(unsigned long eleccionId, unsigned long listaId, string nombreLista) {
	bool res = false;

	char* data = NULL;
	unsigned long data_len = 0;
	bool found = false;

	if (listasEnElecciones->has(eleccionId)) {
		//cout << "contiene reg. para eleccion" << endl;
		found = true;
		if (listasEnElecciones->get(eleccionId,&data,&data_len) == false) {
			cerr << "Error al obtener hash Lista/Eleccion" << endl;
		}
		else {
			char* newData = new char[data_len + sizeof(unsigned long)];
			memcpy(newData,data,data_len);

			free(data);
			data = NULL;
			data = newData;

			char*ptr = reinterpret_cast<char*>(&listaId);
			for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
				data[data_len+i] = *ptr;
				ptr++;
			}

			data_len += sizeof(unsigned long);
		}
	}

	if (found == false) {
	//	cout << "creando registro"<<endl;
		data = new char[sizeof(unsigned long)];
		data_len = 0;

		char*ptr = reinterpret_cast<char*>(&listaId);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len = sizeof(unsigned long);
	}

	// guardo el registro en el hash nuevamente
	res = true;
	if (listasEnElecciones->put(eleccionId,data,data_len) == false) {
		cerr << "Error al actualizar hash Lista/Elecciones" <<endl;
		res = false;
	}

	/// Agrego ademas esta eleccion a la lista de elecciones con igual nombre (y distinto id)
	if (res == true) {
		res = addListaToListaSimilar(nombreLista,listaId);
	}
	return res;
}

vector<unsigned long>CountingMgr::getListasIDsParaLista(string nombre) {
	vector<unsigned long> ids;

	StringUtils su;
	unsigned long convertedId = su.stringKeyToULongKey(nombre);

	char* data = NULL;
	unsigned long data_len = 0;
	if (listasConIgualNombre->get(convertedId,&data,&data_len) == false) {
		return ids;
	}

	for (unsigned int i = 0; i < data_len/sizeof(unsigned long); i++) {
		unsigned long aux = 0;
		char*ptr = reinterpret_cast<char*>(&aux);
		for (unsigned k = 0; k < sizeof(unsigned long); k++) {
			*ptr = data[k+i*sizeof(unsigned long)];
			ptr++;
		}
		ids.push_back(aux);
	}

	free(data);
	return ids;
}

bool CountingMgr::addListaToListaSimilar(string nombreLista, unsigned long listId) {
	StringUtils su;
	unsigned long convertedId = su.stringKeyToULongKey(nombreLista);

	char* data = NULL;
	unsigned long data_len = 0;
	if (listasConIgualNombre->has(convertedId)) {
		if (listasConIgualNombre->get(convertedId,&data,&data_len) == false) {
			cerr << "Error al insertar lista en hash de listas similares" <<endl;
			return false;
		}

		char* newData = new char[data_len + sizeof(unsigned long)];
		memcpy(newData,data,data_len);

		free(data);
		data = NULL;
		data = newData;

		char*ptr = reinterpret_cast<char*>(&listId);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len += sizeof(unsigned long);
	}
	else {
		data = new char[sizeof(unsigned long)];
		data_len = 0;
		char*ptr = reinterpret_cast<char*>(&listId);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}
		data_len += sizeof(unsigned long);
	}

	if (data == NULL) {
		return false;
	}

	bool res = listasConIgualNombre->put(convertedId,data,data_len);;

	free(data);
	return res;
}

vector<unsigned long> CountingMgr::getListasParaEleccion(unsigned long eleccionId) {
	vector<unsigned long> listas;

	char* data = NULL;
	unsigned long data_len = 0;

	if (listasEnElecciones->get(eleccionId,&data,&data_len) == false){
		// no hay entradas en el hash
		cerr << "No se ha encontrada la entrada en el hash Lista/Eleccion" <<endl;
	}
	else {
		for (unsigned int i = 0; i < data_len/sizeof(unsigned long); i++) {
			unsigned long listaId = 0;
			char*ptr = reinterpret_cast<char*>(&listaId);
			for (unsigned j = 0; j < sizeof(unsigned long); j++) {
				*ptr = data[i*sizeof(unsigned long) + j];
				ptr++;
			}
			listas.push_back(listaId);
		}
	}

	if (data != NULL) free(data);
	return listas;
}

vector<Count> CountingMgr::getCountFilteredByEleccion(unsigned long eleccionId) {
	vector<Count> counts;

	char* data = NULL;
	unsigned long data_len = 0;
	bool prueba = filtroElecciones->get(eleccionId,&data,&data_len); 
	if ( prueba == true) {

		for (unsigned i = 0; i < (data_len/sizeof(unsigned long)); i++) {

			unsigned long recId = 0;
			char*ptr = reinterpret_cast<char*>(&recId);
			for (unsigned j = 0; j < sizeof(unsigned long); j++) {
				*ptr = data[i*sizeof(unsigned long) + j];
				ptr++;
			}

			char* record = NULL;
			if (countingFile->getRecordByID(recId,&record)) {

				Count count;

				unsigned long aux = 0;
				char*ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k];
					ptr++;
				}
				count.setEleccionId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+sizeof(unsigned long)];
					ptr++;
				}
				count.setDistritoId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+2*sizeof(unsigned long)];
					ptr++;
				}
				count.setListaId(aux); 

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+3*sizeof(unsigned long)];
					ptr++;
				}
				count.setCount(aux);

				counts.push_back(count);
			}
			if (record != NULL) free(record);
		}
	}

	if (data != NULL) free(data);
	return counts;
}

vector<Count> CountingMgr::getCountFilteredByLista(unsigned long listaId) {
	vector<Count> counts;

	char* data = NULL;
	unsigned long data_len = 0;
	if (filtroListas->get(listaId,&data,&data_len) == true) {

		for (unsigned i = 0; i < (data_len/sizeof(unsigned long)); i++) {

			unsigned long recId = 0;
			char*ptr = reinterpret_cast<char*>(&recId);
			for (unsigned j = 0; j < sizeof(unsigned long); j++) {
				*ptr = data[i*sizeof(unsigned long) + j];
				ptr++;
			}

			char* record = NULL;
			if (countingFile->getRecordByID(recId,&record)) {

				Count count;

				unsigned long aux = 0;
				char*ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k];
					ptr++;
				}
				count.setEleccionId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+sizeof(unsigned long)];
					ptr++;
				}
				count.setDistritoId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+2*sizeof(unsigned long)];
					ptr++;
				}
				count.setListaId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+3*sizeof(unsigned long)];
					ptr++;
				}
				count.setCount(aux);

				counts.push_back(count);
			}
			if (record != NULL) free(record);
		}
	}

	if (data != NULL) free(data);
	return counts;
}

vector<Count> CountingMgr::getCountFilteredByDistrito(unsigned long distritoId) {
	vector<Count> counts;

	char* data = NULL;
	unsigned long data_len = 0;
	
	bool prueba = filtroDistritos->get(distritoId,&data,&data_len); 
	
	if ( prueba == true) {
		for (unsigned i = 0; i < (data_len/sizeof(unsigned long)); i++) {
			unsigned long recId = 0;
			char*ptr = reinterpret_cast<char*>(&recId);
			for (unsigned j = 0; j < sizeof(unsigned long); j++) {
				*ptr = data[i*sizeof(unsigned long) + j];
				ptr++;
			}

			char* record = NULL;
			if (countingFile->getRecordByID(recId,&record)) {

				Count count;

				unsigned long aux = 0;
				char*ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k];
					ptr++;
				}
				count.setEleccionId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+sizeof(unsigned long)];
					ptr++;
				}
				count.setDistritoId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+2*sizeof(unsigned long)];
					ptr++;
				}
				count.setListaId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+3*sizeof(unsigned long)];
					ptr++;
				}
				count.setCount(aux);

				counts.push_back(count);
			}
			if (record != NULL) free(record);
		}
	}

	if (data != NULL) free(data);
	return counts;
}

bool CountingMgr::incrementCountOnRegister(unsigned long eleccionId, unsigned long distritoId, unsigned long listaId) {
	bool res = false;

	char* data = NULL;
	unsigned long data_len = 0;
	if (filtroElecciones->get(eleccionId,&data,&data_len) == true) {

		for (unsigned i = 0; i < (data_len/sizeof(unsigned long)); i++) {

			unsigned long recId = 0;
			char*ptr = reinterpret_cast<char*>(&recId);
			for (unsigned j = 0; j < sizeof(unsigned long); j++) {
				*ptr = data[i*sizeof(unsigned long) + j];
				ptr++;
			}

			char* record = NULL;
			bool found = false;
			unsigned long _vote_count = 0;
			if (countingFile->getRecordByID(recId,&record)) {
				Count count;

				unsigned long aux = 0;
				char*ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k];
					ptr++;
				}
				count.setEleccionId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+sizeof(unsigned long)];
					ptr++;
				}
				count.setDistritoId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+2*sizeof(unsigned long)];
					ptr++;
				}
				count.setListaId(aux);

				ptr = reinterpret_cast<char*>(&aux);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					*ptr = record[k+3*sizeof(unsigned long)];
					ptr++;
				}
				count.setCount(aux);
				_vote_count = count.getCount();

				if (distritoId == count.getDistritoId() && listaId == count.getListaId()) {
					found = true;
				}

			}

			if (record != NULL) free(record);

			if (found == true) {
				// increment
				_vote_count++;

				// save
				char*record = new char[4*sizeof(unsigned long)];

				char*ptr = reinterpret_cast<char*>(&eleccionId);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					record[k] = *ptr;
					ptr++;
				}

				ptr = reinterpret_cast<char*>(&distritoId);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					record[k+sizeof(unsigned long)] = *ptr;
					ptr++;
				}

				ptr = reinterpret_cast<char*>(&listaId);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					record[k+2*sizeof(unsigned long)] = *ptr;
					ptr++;
				}

				ptr = reinterpret_cast<char*>(&_vote_count);
				for (unsigned k = 0; k < sizeof(unsigned long); k++) {
					record[k+3*sizeof(unsigned long)] = *ptr;
					ptr++;
				}

				if (countingFile->updateRecord(recId,record)) {
					res = true;
				}
				else {
					cerr << "Error al actualizar el registro de conteo" <<endl;
					res = false;
				}

				break;
			}
		}
	}
	if (data != NULL) free(data);
	return res;
}

bool CountingMgr::addIdToFiltroDistrito(unsigned long distritoId, unsigned long id) {
	bool res = false;

	char* data = NULL;
	unsigned long data_len;
	bool found = false;

	if (filtroDistritos->has(distritoId)) {
		found = true;
		if (filtroDistritos->get(distritoId,&data,&data_len) == false) {
			cerr << "Error al obtener filtro Distritos" << endl;
		}

		char* newData = new char[data_len + sizeof(unsigned long)];
		memcpy(newData,data,data_len);

		free(data);
		data = NULL;
		data = newData;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len += sizeof(unsigned long);
	}

	if (found == false) {
		data = new char[sizeof(unsigned long)];
		data_len = 0;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len = sizeof(unsigned long);
	}

	// guardo el registro en el hash nuevamente
	res = true;
	if (filtroDistritos->put(distritoId,data,data_len) == false) {
		cerr << "Error al actualizar Filtro Distrito" <<endl;
		res = false;
	}

	return res;
}

bool CountingMgr::addIdToFiltroLista(unsigned long listaId, unsigned long id) {
	bool res = false;

	char* data = NULL;
	unsigned long data_len;
	bool found = false;

	if (filtroListas->has(listaId)) {
		found = true;
		if (filtroListas->get(listaId,&data,&data_len) == false) {
			cerr << "Error al obtener filtro Listas" << endl;
		}

		char* newData = new char[data_len + sizeof(unsigned long)];
		memcpy(newData,data,data_len);

		free(data);
		data = NULL;
		data = newData;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len += sizeof(unsigned long);
	}

	if (found == false) {
		data = new char[sizeof(unsigned long)];
		data_len = 0;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len = sizeof(unsigned long);
	}

	// guardo el registro en el hash nuevamente
	res = true;
	if (filtroListas->put(listaId,data,data_len) == false) {
		cerr << "Error al actualizar Filtro Lista" <<endl;
		res = false;
	}

	return res;
}

bool CountingMgr::addIdToFiltroEleccion(unsigned long eleccionId, unsigned long id) {
	bool res = false;

	char* data = NULL;
	unsigned long data_len;
	bool found = false;

	if (filtroElecciones->has(eleccionId)) {
		found = true;
		if (filtroElecciones->get(eleccionId,&data,&data_len) == false) {
			cerr << "Error al obtener filtro Elecciones" << endl;
		}

		char* newData = new char[data_len + sizeof(unsigned long)];
		memcpy(newData,data,data_len);

		free(data);
		data = NULL;
		data = newData;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len += sizeof(unsigned long);
	}

	if (found == false) {
		data = new char[sizeof(unsigned long)];
		data_len = 0;

		char*ptr = reinterpret_cast<char*>(&id);
		for (unsigned int i = 0; i < sizeof(unsigned long); i++) {
			data[data_len+i] = *ptr;
			ptr++;
		}

		data_len = sizeof(unsigned long);
	}

	// guardo el registro en el hash nuevamente
	res = true;
	if (filtroElecciones->put(eleccionId,data,data_len) == false) {
		cerr << "Error al actualizar Filtro Elecciones" <<endl;
		res = false;
	}

	return res;
}

bool CountingMgr::createCountingRegistersForEleccion(Eleccion *eleccion) {
	bool res = true;

	//para poder agregar los registros, la eleccion no tiene que estar en eleccionesHash

	if (eleccionesHash->has(eleccion->getId())) {
		res = false;
	}
	else {
		// creamos los registros ordenados de la siguiente memoria
		// las listas y los distritos tienen que estar pre cargados
		/// eleccion1-distrito1-lista1;eleccion1-distrito1-lista2;eleccion1-distrito1-lista3;
		/// eleccion1-distrito2-lista1;eleccion1-distrito2-lista2;eleccion1-distrito2-lista3;
		/// eleccion2-distrito1-lista1;eleccion2-distrito1-lista2;
		vector<unsigned long>listasParaEleccion = getListasParaEleccion(eleccion->getId());

		if (listasParaEleccion.size() > 0) {
			for (unsigned int i = 0; i < eleccion->getDistritos().size(); i++) {
				unsigned long distritoId = eleccion->getDistrito(i);

				for (unsigned int j = 0; j < listasParaEleccion.size(); j++) {
					char*record = new char[4*sizeof(unsigned long)];

					unsigned long aux = eleccion->getId();
					char*ptr = reinterpret_cast<char*>(&aux);
					for (unsigned k = 0; k < sizeof(unsigned long); k++) {
						record[k] = *ptr;
						ptr++;
					}

					ptr = reinterpret_cast<char*>(&(distritoId));
					for (unsigned k = 0; k < sizeof(unsigned long); k++) {
						record[k+sizeof(unsigned long)] = *ptr;
						ptr++;
					}

					ptr = reinterpret_cast<char*>(&(listasParaEleccion.at(j)));
					for (unsigned k = 0; k < sizeof(unsigned long); k++) {
						record[k+2*sizeof(unsigned long)] = *ptr;
						ptr++;
					}

					unsigned long count = 0;
					ptr = reinterpret_cast<char*>(&(count));
					for (unsigned k = 0; k < sizeof(unsigned long); k++) {
						record[k+3*sizeof(unsigned long)] = *ptr;
						ptr++;
					}

					unsigned long recordId = 0;
					if (countingFile->addRecord(&recordId,record)) {
						// registro agregado exitosamente

						/// usar este id, para referenciarlo en los indices de reportes por
						/// distrito, lista y eleccion

						addIdToFiltroDistrito(distritoId,recordId);
						addIdToFiltroEleccion(eleccion->getId(),recordId);
						addIdToFiltroLista(listasParaEleccion.at(j),recordId);
					}
					else {
						cerr << "Error al agregar registro en archivo de conteo" << endl;
						res = false;
					}
				}
			}
		}
		else {
			res = false;
		}
	}

	return res;
}
